/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <httpd.h>
#include <http_request.h>
#include <util_md5.h>
#include "WtBasicCmds.h"
#include "WtExecute.h"
#include "WtTable.h"
#include "WtTableUtil.h"
#include "WtMultiTable.h"
#include "WtContext.h"
#include "WtUtil.h"
#include "WtHtmlEntities.h"

/* htmlEncode command */

int WtHtmlEncodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, flags = 0;
  Tcl_UniChar uniCh;
  WtContext *w = WtGetAssocContext(interp);
  const char *str, *p, *q;
  Tcl_Obj *result;

  if (objc == 2 || objc == 3) {
    ret = TCL_OK;

    if (objc == 3) {
      for (p = Tcl_GetString(objv[2]); *p; p = q) {
        Tcl_UtfToUniChar(p, &uniCh);
        q = Tcl_UtfNext(p);
        switch (uniCh) {
          case 'd': flags |= WT_ENT_DEFAULT; break;
          case 'a': flags |= WT_ENT_ALL; break;
          case 's': flags |= WT_ENT_SPECIAL_CHARS; break;
          case 'Q': flags |= WT_ENT_DOUBLE_QUOTE; break;
          case 'q': flags |= WT_ENT_SINGLE_QUOTE; break;
          case 'u': flags |= WT_ENT_UNKNOWN_CHARS; break;
          default:
            Tcl_AppendResult(interp, "Unknown flag: ");
            Tcl_AppendToObj(Tcl_GetObjResult(interp), p, q - p);
            ret = TCL_ERROR;
            break;
        }
        if (ret == TCL_ERROR) {
          break;
        }
      }
    } else {
      flags |= WT_ENT_DEFAULT;
    }

    if (!(str = Tcl_GetString(objv[1]))) {
      ret = TCL_ERROR;
    } else {
      result = WtEncodeHtml(str, flags);
      Tcl_SetObjResult(interp, result);
    }
  } else {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " string flags", NULL);
  }

  return ret;
}

/* apHtmlEncode command */

int WtApHtmlEncodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtContext *w = WtGetAssocContext(interp);

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " string", NULL);
  } else {
    Tcl_SetObjResult(interp,
      WtNewString(WtSafeStr(ap_escape_html(w->web->apReq->pool,
        WtToString(objv[1])))));
    ret = TCL_OK;
  }

  return ret;
}

/* urlEncode command */

int WtUrlEncodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, stringLen, resultLen;
  WtContext *w = WtGetAssocContext(interp);
  const char *str;
  char *result;
  Tcl_Obj *resultObj;

  if (objc == 2) {
    if (str = Tcl_GetStringFromObj(objv[1], &stringLen)) {
      result = WtEncodeUrl(str, stringLen, &resultLen);
      resultObj = Tcl_NewStringObj(result, resultLen);
      ckfree(result);
      Tcl_SetObjResult(interp, resultObj);
      ret = TCL_OK;
    }
  } else {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " string", NULL);
  }

  return ret;
}

/* urlDecode command */

int WtUrlDecodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, rc, stringLen, resultLen;
  WtContext *w = WtGetAssocContext(interp);
  char *str, *result;
  Tcl_Obj *resultObj;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " url", NULL);
  } else {
    str = Tcl_GetStringFromObj(objv[1], &stringLen);
    if (str) {
      result = ckalloc(stringLen + 1);
      memcpy(result, str, stringLen);
      result[stringLen] = '\0';
      resultLen = WtDecodeUrlInPlace(result, stringLen);
      resultObj = Tcl_NewStringObj(result, resultLen);
      ckfree(result);
      Tcl_SetObjResult(interp, resultObj);
      ret = TCL_OK;
    }
  }

  return ret;
}

/* md5Encode command */

int WtMd5EncodeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, len;
  WtContext *w = WtGetAssocContext(interp);
  char *bytes;
  Tcl_Obj *result;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " string", NULL);
  } else {
    bytes = Tcl_GetStringFromObj(objv[1], &len);
    result = Tcl_NewStringObj(
      ap_md5_binary(w->apReq->pool, bytes, len), -1);
    if (result) {
      Tcl_SetObjResult(interp, result);
      ret = TCL_OK;
    }
  }

  return ret;
}

/* type command */

int WtTypeCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtContext *w = WtGetAssocContext(interp);

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " value", NULL);
  } else {
    Tcl_SetObjResult(interp, WtNewString(
      WtSafeStr(objv[1]->typePtr->name)));
    ret = TCL_OK;
  }

  return ret;
}

/* objectInfo command */

int WtObjectInfoCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtContext *w = WtGetAssocContext(interp);
  Tcl_Obj *obj, *list;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " object", NULL);
  } else {
    obj = objv[1];

    list = Tcl_NewListObj(0, NULL);

    Tcl_ListObjAppendElement(interp, list, WtNewString("address"));

    Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj((int)obj));

    Tcl_ListObjAppendElement(interp, list, WtNewString("type"));

    if (obj->typePtr && obj->typePtr->name) {
      Tcl_ListObjAppendElement(interp, list,
        WtNewString(obj->typePtr->name));
    } else {
      Tcl_ListObjAppendElement(interp, list, WtNewString(""));
    }

    Tcl_ListObjAppendElement(interp, list,
      WtNewString("hasValidString"));

    Tcl_ListObjAppendElement(interp, list,
      Tcl_NewBooleanObj(obj->bytes != NULL));

    Tcl_ListObjAppendElement(interp, list, WtNewString("bytes"));

    Tcl_ListObjAppendElement(interp, list,
      WtNewString(WtSafeStr(obj->bytes)));

    Tcl_ListObjAppendElement(interp, list, WtNewString("length"));

    Tcl_ListObjAppendElement(interp, list,
      Tcl_NewIntObj(obj->length));

    Tcl_ListObjAppendElement(interp, list, WtNewString("string"));

    Tcl_ListObjAppendElement(interp, list,
      WtNewString(WtSafeStr(WtToString(obj))));

    Tcl_ListObjAppendElement(interp, list, WtNewString("refCount"));

    Tcl_ListObjAppendElement(interp, list,
      Tcl_NewIntObj(obj->refCount));

    Tcl_SetObjResult(interp, list);

    ret = TCL_OK;
  }

  return ret;
}

/* winDebug command */

int WtWinDebugCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 1) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), NULL);
  } else {
    _asm int 3;
    ret = TCL_OK;
  }

  return ret;
}

int WtBadCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 1) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), NULL);
  } else {
    _asm int 3;
    ret = TCL_OK;
  }

  return ret;
}

/* Initialize commands. */

void WtInitBasicCommands(Tcl_Interp *interp)
{
  WtContext *w = WtGetAssocContext(interp);

  WtLog(HERE, APLOG_DEBUG | APLOG_NOERRNO, w,
    "Wtcl: WtInitBasicCommands.");

  Tcl_CreateObjCommand(interp, "::wt::web::htmlEncode",
    WtHtmlEncodeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::web::apHtmlEncode",
    WtApHtmlEncodeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::web::urlEncode",
    WtUrlEncodeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::web::urlDecode",
    WtUrlDecodeCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::util::md5Encode",
    WtMd5EncodeCmd, NULL, NULL);

  /* Debugging commands */

  Tcl_CreateObjCommand(interp, "::wt::util::objectType", WtTypeCmd,
    NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::util::objectInfo", WtObjectInfoCmd,
    NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::util::winDebug", WtWinDebugCmd,
    NULL, NULL);
}
